package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

type User0 struct {
	Id   int
	Name string
	Age  int
}

var db0 *sql.DB

func init() {
	//parseTime:时间格式转换;
	// loc=Local解决数据库时间少8小时问题
	var err error
	//TODO 注意：一下语句不能使用：db0, err ：=  ;不然将db0当做临时变量来处理，不会对全局变量db0进行赋值。
	db0, err = sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/mygo?charset=utf8&parseTime=true&loc=Local")
	if err != nil {
		log.Fatal("数据库打开出现了问题：", err)
		return
	}
	//	defer db0.Close()
	// 尝试与数据库建立连接（校验dsn是否正确）
	err = db0.Ping()
	if err != nil {
		log.Fatal("数据库连接出现了问题：", err)
		return
	}

}
func main() {
	//queryRowDemo()
	//queryMultiRowDemo()
	//insertRowDemo()
	//updateRowDemo()
	//deleteRowDemo()
	//prepareQueryDemo()
	//prepareInsertDemo()
	//transactionDemo()
	sqlInjectDemo("张三")
}

// 查询单条数据示例
func queryRowDemo() {
	var u User0
	sqlStr := "select id, name, age from user0 where id=?"
	err := db0.QueryRow(sqlStr, 1).Scan(&u.Id, &u.Name, &u.Age)
	if err != nil {
		fmt.Printf("scan failed, err:%v\n", err)
		return
	}
	fmt.Printf("id:%d name:%s age:%d\n", u.Id, u.Name, u.Age)
}

// 查询多条数据示例
func queryMultiRowDemo() {
	sqlStr := "select id, name, age from user0 where id > ?"
	rows, err := db0.Query(sqlStr, 0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	// 非常重要：关闭rows释放持有的数据库链接
	defer rows.Close()

	// 循环读取结果集中的数据
	for rows.Next() {
		var u User0
		err := rows.Scan(&u.Id, &u.Name, &u.Age)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.Id, u.Name, u.Age)
	}
}

// 插入数据
func insertRowDemo() {
	sqlStr := "insert into user0(name, age) values (?,?)"
	ret, err := db0.Exec(sqlStr, "王五", 22)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	theID, err := ret.LastInsertId() // 新插入数据的id
	if err != nil {
		fmt.Printf("get lastinsert ID failed, err:%v\n", err)
		return
	}
	fmt.Printf("insert success, the id is %d.\n", theID)
}

// 更新数据
func updateRowDemo() {
	sqlStr := "update user0 set age=? where id = ?"
	ret, err := db0.Exec(sqlStr, 22, 3)
	if err != nil {
		fmt.Printf("update failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操作影响的行数
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("update success, affected rows:%d\n", n)
}

// 删除数据
func deleteRowDemo() {
	sqlStr := "delete from user0 where id = ?"
	ret, err := db0.Exec(sqlStr, 3)
	if err != nil {
		fmt.Printf("delete failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操作影响的行数
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("delete success, affected rows:%d\n", n)
}

/*
Go中的
func (db *DB) Prepare(query string) (*Stmt, error)
Prepare方法会先将sql语句发送给MySQL服务端，返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。
*/
// 预处理查询示例
func prepareQueryDemo() {
	sqlStr := "select id, name, age from user0 where id > ?"
	stmt, err := db0.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	// 循环读取结果集中的数据
	for rows.Next() {
		var u User0
		err := rows.Scan(&u.Id, &u.Name, &u.Age)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.Id, u.Name, u.Age)
	}
}

/*
插入、更新和删除操作的预处理十分类似，这里以插入操作的预处理为例：
*/
// 预处理插入示例
func prepareInsertDemo() {
	sqlStr := "insert into user0(name, age) values (?,?)"
	stmt, err := db0.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec("小王子", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	_, err = stmt.Exec("沙河娜扎", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	fmt.Println("insert success.")
}

/*
Go语言中使用以下三个方法实现MySQL中的事务操作。 开始事务
func (db *DB) Begin() (*Tx, error)
提交事务
func (tx *Tx) Commit() error
回滚事务
func (tx *Tx) Rollback() error
*/
// 事务操作示例
func transactionDemo() {
	tx, err := db0.Begin() // 开启事务
	if err != nil {
		if tx != nil {
			_ = tx.Rollback() // 回滚
		}
		fmt.Printf("begin trans failed, err:%v\n", err)
		return
	}
	sqlStr1 := "Update user0 set age=28 where id=?"
	_, err = tx.Exec(sqlStr1, 4)
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec sql1 failed, err:%v\n", err)
		return
	}
	sqlStr2 := "Update user0 set age=29 where id=?"
	_, err = tx.Exec(sqlStr2, 5)
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec sql2 failed, err:%v\n", err)
		return
	}
	err = tx.Commit() // 提交事务
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("commit failed, err:%v\n", err)
		return
	}
	fmt.Println("exec trans success!")
}

/*
SQL注入
todo :  我们任何时候都不应该自己拼接SQL语句！
*/
// sql注入示例
func sqlInjectDemo(name string) {
	sqlStr := fmt.Sprintf("select id, name, age from user0 where name='%s'", name)
	fmt.Printf("SQL:%s\n", sqlStr)
	// todo :正确写法 sqlStr := "select id, name, age from user0 where name=?"
	var u User0
	err := db0.QueryRow(sqlStr).Scan(&u.Id, &u.Name, &u.Age)
	if err != nil {
		fmt.Printf("scan failed, err:%v\n", err)
		return
	}
	fmt.Printf("id:%d name:%s age:%d\n", u.Id, u.Name, u.Age)
}
